/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.buaa.act.gephi.plugin.gui;

import edu.buaa.act.gephi.plugin.gui.lib.RangeSlider;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.io.File;
import java.util.Calendar;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import edu.buaa.act.gephi.plugin.task.BuildDatabaseAsyncTask;
import edu.buaa.act.gephi.plugin.task.ReadNetworkFileTask;
import edu.buaa.act.gephi.plugin.task.GetTemporalDataFileListAsyncTask;
import org.act.neo4j.temporal.demo.utils.Helper;
import org.act.neo4j.temporal.demo.vo.RoadChain;
import org.gephi.utils.longtask.api.LongTaskExecutor;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages;

/**
 * Top component which user can build a demo TGraph database from source.
 */
@ConvertAsProperties(
        dtd = "-//edu.buaa.act.gephi.plugin.gui//DataBaseBuilder//EN",
        autostore = false
)
@TopComponent.Description(
        preferredID = "DataBaseBuilderTopComponent",
        //iconBase="SET/PATH/TO/ICON/HERE", 
        persistenceType = TopComponent.PERSISTENCE_ALWAYS
)
@TopComponent.Registration(mode = "Expression MODE is undefined on line 28, column 38 in Templates/NetBeansModuleDevelopment-files/templateTopComponent637.java.", openAtStartup = false)
@ActionID(category = "Window", id = "edu.buaa.act.gephi.plugin.gui.DataBaseBuilderTopComponent")
@ActionReference(path = "Menu/Plugins" , position = 333)
@TopComponent.OpenActionRegistration(
        displayName = "#CTL_DataBaseBuilderAction",
        preferredID = "DataBaseBuilderTopComponent"
)
@Messages({
    "CTL_DataBaseBuilderAction=TGraph Demo DB Builder",
    "CTL_DataBaseBuilderTopComponent=TGraph DataBase Builder",
    "HINT_DataBaseBuilderTopComponent=Build a fresh demo TGraph dataBase"
})
public final class DataBaseBuilderTopComponent extends TopComponent {

    private File networkFile;
    private File dbDirectory;
    private File dataDirectory;
    private List<File> dataFileList;
    private List<RoadChain> roadList;

    public DataBaseBuilderTopComponent() {
        initComponents();
        setName(Bundle.CTL_DataBaseBuilderTopComponent());
        setToolTipText(Bundle.HINT_DataBaseBuilderTopComponent());
        putClientProperty(TopComponent.PROP_DRAGGING_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_MAXIMIZATION_DISABLED, Boolean.TRUE);
        putClientProperty(TopComponent.PROP_SLIDING_DISABLED, Boolean.TRUE);
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        Button_chooseNetworkFile = new javax.swing.JButton();
        jLabel2 = new javax.swing.JLabel();
        Button_chooseDBPath = new javax.swing.JButton();
        jLabel3 = new javax.swing.JLabel();
        Button_chooseDataFolder = new javax.swing.JButton();
        Button_buildControl = new javax.swing.JButton();
        Label_netFile = new javax.swing.JLabel();
        Panel_rangeSlider = new javax.swing.JPanel();
        Label_dbFolder = new javax.swing.JLabel();
        Label_dataFolder = new javax.swing.JLabel();
        Label_timeRange = new javax.swing.JLabel();
        Label_fileCount = new javax.swing.JLabel();

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.jLabel1.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(Button_chooseNetworkFile, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Button_chooseNetworkFile.text")); // NOI18N
        Button_chooseNetworkFile.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                Button_chooseNetworkFileMouseClicked(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.jLabel2.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(Button_chooseDBPath, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Button_chooseDBPath.text")); // NOI18N
        Button_chooseDBPath.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                Button_chooseDBPathMouseClicked(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.jLabel3.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(Button_chooseDataFolder, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Button_chooseDataFolder.text")); // NOI18N
        Button_chooseDataFolder.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                Button_chooseDataFolderMouseClicked(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(Button_buildControl, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Button_buildControl.text")); // NOI18N
        Button_buildControl.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                Button_buildControlMouseClicked(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(Label_netFile, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Label_netFile.text")); // NOI18N

        Panel_rangeSlider.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Panel_rangeSlider.border.title"))); // NOI18N

        RangeSlider_timeSelection = new RangeSlider();
        RangeSlider_timeSelection.setEnabled(false);
        RangeSlider_timeSelection.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                RangeSlider_timeSelectionStateChanged(e);
            }
        });
        Label_minTime = new javax.swing.JLabel("min time");
        Label_maxTime = new javax.swing.JLabel("max time");

        javax.swing.GroupLayout Panel_rangeSliderLayout = new javax.swing.GroupLayout(Panel_rangeSlider);
        Panel_rangeSlider.setLayout(Panel_rangeSliderLayout);
        Panel_rangeSliderLayout.setHorizontalGroup(
            Panel_rangeSliderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 366, Short.MAX_VALUE)
        );
        Panel_rangeSliderLayout.setVerticalGroup(
            Panel_rangeSliderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 0, Short.MAX_VALUE)
        );

        Panel_rangeSlider.setLayout(new BorderLayout(3,3));
        RangeSlider_timeSelection.setPreferredSize(new Dimension(240, RangeSlider_timeSelection.getPreferredSize().height));

        org.openide.awt.Mnemonics.setLocalizedText(Label_dbFolder, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Label_dbFolder.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(Label_dataFolder, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Label_dataFolder.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(Label_timeRange, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Label_timeRange.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(Label_fileCount, org.openide.util.NbBundle.getMessage(DataBaseBuilderTopComponent.class, "DataBaseBuilderTopComponent.Label_fileCount.text")); // NOI18N

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel1)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(Label_netFile)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(Button_chooseNetworkFile))
                        .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel2)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(Label_dbFolder)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(Button_chooseDBPath))
                        .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel3)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(Label_dataFolder)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 140, Short.MAX_VALUE)
                                .addComponent(Button_chooseDataFolder))
                        .addGroup(layout.createSequentialGroup()
                                .addComponent(Button_buildControl)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(Label_timeRange)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(Label_fileCount)
                                .addGap(0, 0, Short.MAX_VALUE))
                        .addComponent(Panel_rangeSlider, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(jLabel1)
                        .addComponent(Button_chooseNetworkFile)
                        .addComponent(Label_netFile))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(jLabel2)
                        .addComponent(Button_chooseDBPath)
                        .addComponent(Label_dbFolder))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(jLabel3)
                        .addComponent(Button_chooseDataFolder)
                        .addComponent(Label_dataFolder))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(Panel_rangeSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(Button_buildControl)
                        .addComponent(Label_timeRange)
                        .addComponent(Label_fileCount))
                .addContainerGap(130, Short.MAX_VALUE))
        );

        Panel_rangeSlider.add(RangeSlider_timeSelection, BorderLayout.CENTER);
        Panel_rangeSlider.add(Label_minTime, BorderLayout.WEST);
        Panel_rangeSlider.add(Label_maxTime, BorderLayout.EAST);
    }// </editor-fold>//GEN-END:initComponents

    private void Button_buildControlMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_Button_buildControlMouseClicked
        if(dataDirectory!=null && dataFileList!=null && roadList!=null){
            int start = RangeSlider_timeSelection.getValue();
            int end = RangeSlider_timeSelection.getUpperValue();
            BuildDatabaseAsyncTask task = new BuildDatabaseAsyncTask(
                    roadList,
                    dataFileList.subList(getFileIndex(start, dataFileList), getFileIndex(end, dataFileList)),
                    dbDirectory
            );
            new LongTaskExecutor(true).execute(task,task,"Building database...",null);
        }
    }//GEN-LAST:event_Button_buildControlMouseClicked

    private void Button_chooseNetworkFileMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_Button_chooseNetworkFileMouseClicked
        JFileChooser fc = new JFileChooser();
//        fc.setFileFilter(new TGraphDatabaseFolderFilter());
        fc.setDialogTitle("choose road network file (Topo.csv)");
        fc.setDialogType(JFileChooser.OPEN_DIALOG);
        if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            networkFile = fc.getSelectedFile();
            if (networkFile.exists()) {
                ReadNetworkFileTask readNetTask = new ReadNetworkFileTask(networkFile){
                    @Override
                    protected void onResult(List<RoadChain> list){
                        roadList = list;
                        Label_netFile.setText(list.size()+" roads.");
                    }
                };
                new LongTaskExecutor(true).execute(readNetTask, readNetTask, "reading...", null);
            }else{
                throw new RuntimeException("TGraph: network file not exist");
            }
        }
    }//GEN-LAST:event_Button_chooseNetworkFileMouseClicked

    private void Button_chooseDBPathMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_Button_chooseDBPathMouseClicked
        JFileChooser fc = new JFileChooser();
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        fc.setDialogTitle("choose a folder where the database will be created");
        fc.setDialogType(JFileChooser.SAVE_DIALOG);
        if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            dbDirectory = fc.getSelectedFile();
            if (dbDirectory.exists() && dbDirectory.isDirectory()) {
                int len = dbDirectory.listFiles().length;
                if(len>0){
                    if(confirm("folder not empty, all content in it will be delete, continue?")){
                        Helper.deleteAllFilesOfDir(dbDirectory);
                        dbDirectory.mkdir();
                    }
                }
                Label_dbFolder.setText(dbDirectory.getAbsolutePath());
//                ReadNetworkFileTask readNetTask = new ReadNetworkFileTask();
//                new LongTaskExecutor(true).execute(readNetTask, readNetTask, "reading...", null);
            }else{
                throw new RuntimeException("TGraph: network file not exist");
            }
        }
    }//GEN-LAST:event_Button_chooseDBPathMouseClicked

    private void Button_chooseDataFolderMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_Button_chooseDataFolderMouseClicked
        JFileChooser fc = new JFileChooser();
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        fc.setDialogTitle("choose folder contains temporal data(csv) files");
        fc.setDialogType(JFileChooser.OPEN_DIALOG);
        if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            dataDirectory = fc.getSelectedFile();
            if (dataDirectory.exists()) {
                GetTemporalDataFileListAsyncTask task = new GetTemporalDataFileListAsyncTask(dataDirectory){
                    @Override
                    protected void onResult(List<File> value){
                        dataFileList = value;
                        int first = Helper.getFileTimeStamp(value.get(0));
                        int last = Helper.getFileTimeStamp(value.get(value.size()-1));
                        String firstStr = Helper.timeStamp2String(first);
                        String lastStr = Helper.timeStamp2String(last);
                        Label_minTime.setText(firstStr);
                        Label_maxTime.setText(lastStr);
                        Label_timeRange.setText(firstStr + " ~ " + lastStr);
                        Label_fileCount.setText("including " + value.size() + " files");
                        RangeSlider_timeSelection.setMinimum(first);
                        RangeSlider_timeSelection.setMaximum(last);
                        RangeSlider_timeSelection.setValue(first);
                        RangeSlider_timeSelection.setUpperValue(last);
                        RangeSlider_timeSelection.setEnabled(true);
                    }
                };
                new LongTaskExecutor(true).execute(task, task, "reading...", null);
            }else{
                throw new RuntimeException("TGraph: network file not exist");
            }
        }
    }//GEN-LAST:event_Button_chooseDataFolderMouseClicked

    private void RangeSlider_timeSelectionStateChanged(ChangeEvent e){
        RangeSlider slider = (RangeSlider) e.getSource();
        Label_timeRange.setText(
                Helper.timeStamp2String(slider.getValue()) + " ~ " +
                        Helper.timeStamp2String(slider.getUpperValue()));
        Label_fileCount.setText("including " + countFiles(slider.getValue(), slider.getUpperValue()) + " files");
    }

    private int countFiles(int startTimestamp, int endTimestamp) {
        int startFileIndex = getFileIndex(startTimestamp, dataFileList);
        int endFileIndex = getFileIndex(endTimestamp, dataFileList);
        return endFileIndex-startFileIndex+1;
    }

    private int getFileIndex(int timestamp, List<File> fList){
        String fileName = timeStamp2FileName(timestamp);
        for(int i=0;i<fList.size();i++){
            File f = fList.get(i);
            if(f.getName().compareTo(fileName)>=0){
//                System.out.println(fileName+"->"+f.getName());
                return i;
            }
        }
        return fList.size()-1;
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton Button_buildControl;
    private javax.swing.JButton Button_chooseDBPath;
    private javax.swing.JButton Button_chooseDataFolder;
    private javax.swing.JButton Button_chooseNetworkFile;
    private javax.swing.JLabel Label_dataFolder;
    private javax.swing.JLabel Label_dbFolder;
    private javax.swing.JLabel Label_fileCount;
    private javax.swing.JLabel Label_netFile;
    private javax.swing.JLabel Label_timeRange;
    private javax.swing.JPanel Panel_rangeSlider;
    private RangeSlider RangeSlider_timeSelection;
    private javax.swing.JLabel Label_minTime;
    private javax.swing.JLabel Label_maxTime;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    // End of variables declaration//GEN-END:variables
    @Override
    public void componentOpened() {
        // TODO add custom code on component opening
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }

    private void notice(String s) {
        DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(s));
    }

    private boolean confirm(String s){
        if(DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation(s,NotifyDescriptor.OK_CANCEL_OPTION)) == NotifyDescriptor.OK_OPTION){
            return true;
        } else {
            return false;
        }
    }

    public static String timeStamp2FileName(final int timestamp){
        Calendar ca= Calendar.getInstance();
        ca.setTimeInMillis(((long) timestamp) * 1000);
        return "TJamData_"+ca.get(Calendar.YEAR)+
                String.format("%02d",(ca.get(Calendar.MONTH)+1))+
                String.format("%02d", ca.get(Calendar.DAY_OF_MONTH))+
                String.format("%02d", ca.get(Calendar.HOUR_OF_DAY))+
                String.format("%02d", ca.get(Calendar.MINUTE))+".csv";
    }
}
